#ifndef __CCULLRENDERER__
#define __CCULLRENDERER__

#include "VMath.hpp"

#if !defined(PS3)
//#define CULL_RENDERER_REPROJ_DEBUG
#endif
#define CULL_RENDERER_MINZ

// enable this define to allow ingame debugging of the coverage buffer
//#define CULLING_ENABLE_DEBUG_OVERLAY

extern	SHWOccZBuffer HWZBuffer;


#pragma warning(push)
#pragma warning(disable:6262)
#pragma warning(disable:4554)

namespace NAsyncCull {
namespace Debug {

inline void Draw2DBox( float fX, float fY, float fHeigth, float fWidth, const ColorB &rColor, float fScreenHeigth, float fScreenWidth, IRenderAuxGeom *pAuxRenderer)
{		
		
	float fPosition[4][2] = {	{ fX,						fY },
														{ fX,						fY + fHeigth },
														{ fX + fWidth,	fY + fHeigth },
														{ fX + fWidth, fY} };

	// compute normalized position from absolute points
	Vec3 vPosition[4] = { Vec3(fPosition[0][0]/fScreenWidth, fPosition[0][1]/fScreenHeigth, 0.0f),
												Vec3(fPosition[1][0]/fScreenWidth, fPosition[1][1]/fScreenHeigth, 0.0f),
												Vec3(fPosition[2][0]/fScreenWidth, fPosition[2][1]/fScreenHeigth, 0.0f),
												Vec3(fPosition[3][0]/fScreenWidth, fPosition[3][1]/fScreenHeigth, 0.0f) };

	uint16 const anTriangleIndices[6] = { 0, 1, 2, 
																				0, 2, 3	};	

	pAuxRenderer->DrawTriangles( vPosition, 4, anTriangleIndices, 6, rColor );	
}

} // namesapce Debug
} //namespace NasyncCull

namespace NAsyncCull
{

typedef float											tdZexel;
typedef uint16										tdIndex;
#ifdef PS3
	typedef NVMath::vec4*						tdVertexCache;
	typedef tdVertexCache						tdVertexCacheArg;
#else
	typedef PodArray<NVMath::vec4>&	tdVertexCacheArg;
	typedef PodArray<NVMath::vec4>	tdVertexCache;
#endif
enum{VERTEX_CACHE_COUNT=4*1024/sizeof(NVMath::vec4)};


template<uint32 SIZEX,uint32 SIZEY>
class  CCullRenderer
{
	DEFINE_ALIGNED_DATA( tdZexel, m_ZBuffer[SIZEX*SIZEY], 128 );
#if !defined(PS3)
	DEFINE_ALIGNED_DATA( tdZexel, m_ZBufferSwap[SIZEX*SIZEY], 128 ); // 128 byte for XMemSet128
#endif

#ifdef CULL_RENDERER_REPROJ_DEBUG
	tdZexel								m_ZBufferOrig[SIZEX*SIZEY];
#endif

	NVMath::vec4					m_VMaxXY		_ALIGN(16);
	
	uint32								m_SizeX4;




	uint32								m_DrawCall;
	uint32								m_PolyCount;

#if defined(CULLING_ENABLE_DEBUG_OVERLAY)
	static float m_FrozenDephtBuffer[SIZEX*SIZEY];
	static float m_FrozenDephtBufferReprojected[SIZEX*SIZEY];	
#endif

	template<bool WRITE,bool CULL>
	bool 									Triangle(	const  NVMath::vec4& rV0,
																	const  NVMath::vec4& rV1,
																	const  NVMath::vec4& rV2)
												{
													using namespace NVMath;
													vec4 V0		=	rV0;
													vec4 V1		=	rV1;
													vec4 V2		=	rV2;

													const	uint32	Idx	=	SignMask(Shuffle<xzzz>(Shuffle<zzzz>(V0,V1),V2))&(BitX|BitY|BitZ);
													if(Idx==(BitX|BitY|BitZ))
														return false;

													bool Visible=false;

													switch(Idx)
													{
														case 0:
														break;
														case BitX:
														{
															const vec4	F0	=	Swizzle<zzzz>(V0);
															const vec4	F1	=	Swizzle<zzzz>(V1);
															const vec4	F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F0,Sub(F0,F2));
															const vec4	M1	=	Div(F0,Sub(F0,F1));
															const vec4	P0	=	Madd(Sub(V2,V0),M0,V0);
															const vec4	P1	=	Madd(Sub(V1,V0),M1,V0);
															Visible=Triangle2D<WRITE,CULL>(P0,P1,V1);
															V0	=	P0;
														}
														break;
														case BitY:
														{
															const vec4	F0	=	Swizzle<zzzz>(V0);
															const vec4	F1	=	Swizzle<zzzz>(V1);
															const vec4	F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F1,Sub(F1,F0));
															const vec4	M1	=	Div(F1,Sub(F1,F2));
															const vec4	P0	=	Madd(Sub(V0,V1),M0,V1);
															const vec4	P1	=	Madd(Sub(V2,V1),M1,V1);
															Visible=Triangle2D<WRITE,CULL>(P0,P1,V2);
															V1	=	P0;
														}
														break;
														case BitX|BitY:
														{
															const vec4 	F0	=	Swizzle<zzzz>(V0);
															const vec4 	F1	=	Swizzle<zzzz>(V1);
															const vec4 	F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F0,Sub(F0,F2));
															const vec4	M1	=	Div(F1,Sub(F1,F2));
															V0	=	Madd(Sub(V2,V0),M0,V0);
															V1	=	Madd(Sub(V2,V1),M1,V1);
														}
														break;
														case BitZ:
														{
															const vec4	F0	=	Swizzle<zzzz>(V0);
															const vec4	F1	=	Swizzle<zzzz>(V1);
															const vec4	F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F2,Sub(F2,F1));
															const vec4	M1	=	Div(F2,Sub(F2,F0));
															const vec4	P0	=	Madd(Sub(V1,V2),M0,V2);
															const vec4	P1	=	Madd(Sub(V0,V2),M1,V2);
															Visible=Triangle2D<WRITE,CULL>(V0,P0,P1);
															V2	=	P0;
														}
														break;
														case BitX|BitZ:
														{
															const vec4 	F0	=	Swizzle<zzzz>(V0);
															const vec4 	F1	=	Swizzle<zzzz>(V1);
															const vec4 	F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F0,Sub(F0,F1));
															const vec4	M1	=	Div(F2,Sub(F2,F1));
															V0	=	Madd(Sub(V1,V0),M0,V0);
															V2	=	Madd(Sub(V1,V2),M1,V2);
														}
														break;
														case BitY|BitZ:
														{
															const vec4 F0	=	Swizzle<zzzz>(V0);
															const vec4 F1	=	Swizzle<zzzz>(V1);
															const vec4 F2	=	Swizzle<zzzz>(V2);
															const vec4	M0	=	Div(F1,Sub(F1,F0));
															const vec4	M1	=	Div(F2,Sub(F2,F0));
															V1	=	Madd(Sub(V0,V1),M0,V1);
															V2	=	Madd(Sub(V0,V2),M1,V2);
														}
														break;
														case BitX|BitY|BitZ:
														break;
#if !defined(PS3) && !defined(CAFE)
														default:
															__assume(0);
#endif
													}
													return Visible|Triangle2D<WRITE,CULL>(V0,V1,V2);
												}


	template<bool WRITE,bool CULL>
	bool										Triangle2D(NVMath::vec4 rV0,NVMath::vec4 rV1,NVMath::vec4 rV2)
												{
													using namespace NVMath;
													const	vec4	WWW		=	Shuffle<xzww>(Shuffle<wwww>(rV0,rV1),rV2);
													const	vec4	iWWW	=	Rcp(WWW);

													vec4 V0	=	Mul(rV0,Swizzle<xxxx>(iWWW));
													vec4 V1	=	Mul(rV1,Swizzle<yyyy>(iWWW));
													vec4 V2	=	Mul(rV2,Swizzle<zzzz>(iWWW));

													vec4 V210	=	Sub(Shuffle<xyxy>(V1,V2),Swizzle<xyxy>(V0));
													vec4 Det	=	Mul(V210,Swizzle<wzwz>(V210));
													Det			=	Sub(Det,Swizzle<yyyy>(Det));

													if((SignMask(CmpLE(Det,Vec4Epsilon()))&BitX)!=0)
														return false;

													vec4 VMax	=	Max(Max(V0,V1),V2);
													vec4 VMin	=	Min(Min(V0,V1),V2);
													VMax		=	Add(VMax,Vec4One());
													//saturate to 0 - ScreenSize cause it's assigned to uin16
													VMin		=	Min(VMin,m_VMaxXY);
													VMax		=	Min(VMax,m_VMaxXY);

																vec4	VMinMax	=	floatToint32(Max(Shuffle<xyxy>(VMin,VMax),Vec4Zero()));
																uint16	MinX	=	Vec4int32(VMinMax,0);
													const uint16	MinY	=	Vec4int32(VMinMax,1);
													const uint16	MaxX	=	Vec4int32(VMinMax,2);
													const uint16	MaxY	=	Vec4int32(VMinMax,3);
													if(MinX>=MaxX||MinY>=MaxY)
														return false;
													MinX&=~3;

#ifdef CULL_RENDERER_MINZ
													const vec4	VMinZ	=	Swizzle<zzzz>(Min(Min(rV0,rV1),rV2));
#endif
													const vec4	V0z	=	Swizzle<zzzz>(rV0);
													const vec4	Z10	=	Sub(Swizzle<zzzz>(rV1),V0z);
													const vec4	Z20	=	Sub(Swizzle<zzzz>(rV2),V0z);
													//const vec4	V0w	=	Swizzle<wwww>(V0);
													//const vec4	W10	=	Sub(Swizzle<wwww>(V1),V0w);
													//const vec4	W20	=	Sub(Swizzle<wwww>(V2),V0w);

													V210			=	Div(V210,Swizzle<xxxx>(Det));
													const vec4 X20	=	Swizzle<xxxx>(V210);
													const vec4 Y20	=	Swizzle<yyyy>(V210);
													const vec4 X10	=	Swizzle<zzzz>(V210);
													const vec4 Y10	=	Swizzle<wwww>(V210);

													//VMinMax	=	int32Tofloat(VMinMax);
													const vec4	dx4	=	Sub(Add(NVMath::Vec4(static_cast<float>(MinX)),Vec4ZeroOneTwoThree()),Swizzle<xxxx>(V0));
													//const vec4	dx4	=	Add(Swizzle<xxxx>(Sub(VMinMax,V0)),Vec4ZeroOneTwoThree());
													const vec4	Y1x	=	Mul(Y10,dx4);
													const vec4	Y2x	=	Mul(Y20,dx4);
																vec4	dy4	=	Sub(NVMath::Vec4(static_cast<float>(MinY)),Swizzle<yyyy>(V0));
																//vec4	dy4	=	Swizzle<yyyy>(Sub(VMinMax,V0));
													const vec4	Y14	=	Mul(Y10,Vec4Four());
													const vec4	Y24	=	Mul(Y20,Vec4Four());
													const vec4	Y34	=	Sub(Y14,Y24);
													vec4 Visible	=	Vec4FFFFFFFF();
													for(uint16 y=MinY;y<MaxY;y++,dy4=Add(dy4,Vec4One()))	//for stability disabled TV0=Add(TV0,X10),TV1=Add(TV1,X20))
													{
														vec4	Px	=	Sub(Y1x,Mul(X10,dy4));
														vec4	Py	=	Sub(Mul(X20,dy4),Y2x);
														vec4 	Pz	=	Sub(Sub(Vec4One(),Py),Px);

														vec4* 	pDstZ	=	reinterpret_cast<vec4*>(&m_ZBuffer[MinX+y*(uint16)SIZEX]);
														for(uint16 x=MinX;x<MaxX;)	//for stability disabled TV0=Add(TV0,X10),TV1=Add(TV1,X20))
														{
															Prefetch<ECL_LVL1>(pDstZ);
															vec4 	Mask		=	Or(Or(Px,Py),Pz);
															vec4 Z,		rZ	=	*pDstZ;
#ifdef CULL_RENDERER_MINZ
															if(!WRITE)
																Mask		=	Or(Mask,CmpLE(rZ,VMinZ));
															else
#endif
															{
																Z				=	Madd(Z10,Px,Madd(Z20,Py,V0z));
																Mask		=	Or(Mask,CmpLE(rZ,Z));
															}
															Visible	=	And(Visible,Mask);
															if(WRITE)	//compile time
																*pDstZ	=	SelectSign(Z,rZ,Mask);
															x				+=	4;
															Px			=	Add(Px,Y14);
															Py			=	Sub(Py,Y24);
															Pz			=	Sub(Pz,Y34);
															pDstZ++;
														}
														if(!WRITE && SignMask(Visible)!=(BitX|BitY|BitZ|BitW))
															return true;
													}
													//for(uint16 y=MinY;y<MaxY;y++,dy4=Add(dy4,Vec4One()))	//for stability disabled TV0=Add(TV0,X10),TV1=Add(TV1,X20))
													//{
													//	vec4	Px	=	Sub(Y1x,Mul(X10,dy4));
													//	vec4	Py	=	Sub(Mul(X20,dy4),Y2x);
													//	vec4 	Pz	=	Sub(Sub(Vec4One(),Py),Px);
													//	uint16 x		=	MinX;

													//	vec4 	Mask;
													//	do
													//	{
													//		if(SignMask(Mask=Or(Or(Px,Py),Pz))!=(BitX|BitY|BitZ|BitW))
													//		{
													//			vec4* 	pDstZ	=	reinterpret_cast<vec4*>(&m_ZBuffer[x+y*(uint16)SIZEX]);
													//			Prefetch<ECL_LVL1>(pDstZ);
													//			do
													//			{
													//				vec4 Z	=	Madd(Z10,Px,Madd(Z20,Py,V0z));
													//				vec4 rZ	=	*pDstZ;
													//				Mask		=	Or(Mask,CmpLE(rZ,Z));
													//				Visible	=	And(Visible,Mask);
													//				if(WRITE)	//compile time
													//					*pDstZ	=	SelectSign(Z,rZ,Mask);
													//				pDstZ++;
													//				Px			=	Add(Px,Y14);
													//				Py			=	Sub(Py,Y24);
													//				Pz			=	Sub(Pz,Y34);
													//				Mask		=	Or(Or(Px,Py),Pz);
													//				x				+=	4;
													//			}while(x<MaxX);
													//			break;
													//		}
													//		if(!WRITE && SignMask(Visible)!=(BitX|BitY|BitZ|BitW))
													//			return true;
													//		Px	=	Add(Px,Y14);
													//		Py	=	Sub(Py,Y24);
													//		Pz	=	Sub(Pz,Y34);
													//		x	+=	4;
													//	}while(x<MaxX);
													//}
													return SignMask(Visible)!=(BitX|BitY|BitZ|BitW);
												}


	ILINE	bool						Quad2D(const NVMath::vec4& rV0,const NVMath::vec4& rV1,const NVMath::vec4& rV3,const NVMath::vec4& rV2)
												{
													using namespace NVMath;
													const	vec4	WWW		=	Shuffle<xzxz>(Shuffle<wwww>(rV0,rV1),Shuffle<wwww>(rV2,rV3));
													const	vec4	iWWW	=	Rcp(WWW);

													vec4 V0	=	Mul(rV0,Swizzle<xxxx>(iWWW));
													vec4 V1	=	Mul(rV1,Swizzle<yyyy>(iWWW));
													vec4 V2	=	Mul(rV2,Swizzle<zzzz>(iWWW));
													vec4 V3	=	Mul(rV3,Swizzle<wwww>(iWWW));

													vec4 V210	=	Sub(Shuffle<xyxy>(V1,V2),Swizzle<xyxy>(V0));
													vec4 V213	=	Sub(Shuffle<xyxy>(V1,V2),Swizzle<xyxy>(V3));
													vec4 Det	=	Mul(V210,Swizzle<wzwz>(V210));
													Det				=	Sub(Det,Swizzle<yyyy>(Det));

													vec4 VMax	=	Max(Max(V0,V1),Max(V2,V3));
													vec4 VMin	=	Min(Min(V0,V1),Min(V2,V3));
													VMax		=	Add(VMax,Vec4One());
													//saturate to 0 - ScreenSize cause it's assigned to uin16
													VMin		=	Min(VMin,m_VMaxXY);
													VMax		=	Min(VMax,m_VMaxXY);

													vec4	VMinMax	=	floatToint32(Max(Shuffle<xyxy>(VMin,VMax),Vec4Zero()));
													uint16	MinX	=	Vec4int32(VMinMax,0);
													const uint16	MinY	=	Vec4int32(VMinMax,1);
													const uint16	MaxX	=	Vec4int32(VMinMax,2);
													const uint16	MaxY	=	Vec4int32(VMinMax,3);
													if(MinX>=MaxX||MinY>=MaxY)
														return false;
													MinX&=~3;

													const vec4	VMinZ	=	Swizzle<zzzz>(Min(Min(rV0,rV1),Min(rV2,rV3)));
													Det		=	Rcp(Swizzle<xxxx>(Det));
													V210	=	Mul(V210,Det);
													V213	=	Mul(V213,Det);
													const vec4 X20	=	Swizzle<xxxx>(V210);
													const vec4 Y20	=	Swizzle<yyyy>(V210);
													const vec4 X10	=	Swizzle<zzzz>(V210);
													const vec4 Y10	=	Swizzle<wwww>(V210);
													const vec4 X23	=	Swizzle<xxxx>(V213);
													const vec4 Y23	=	Swizzle<yyyy>(V213);
													const vec4 X13	=	Swizzle<zzzz>(V213);
													const vec4 Y13	=	Swizzle<wwww>(V213);

													//VMinMax	=	int32Tofloat(VMinMax);
													const vec4	dx4	=	Sub(Add(NVMath::Vec4(static_cast<float>(MinX)),Vec4ZeroOneTwoThree()),Swizzle<xxxx>(V0));
													//const vec4	dx4	=	Add(Swizzle<xxxx>(Sub(VMinMax,V0)),Vec4ZeroOneTwoThree());
													const vec4	Y10x	=	Mul(Y10,dx4);
													const vec4	Y20x	=	Mul(Y20,dx4);
													const vec4	Y13x	=	Mul(Y13,dx4);
													const vec4	Y23x	=	Mul(Y23,dx4);
													vec4	dy4	=	Sub(NVMath::Vec4(static_cast<float>(MinY)),Swizzle<yyyy>(V0));
													//vec4	dy4	=	Swizzle<yyyy>(Sub(VMinMax,V0));
													const vec4	Y104	=	Mul(Y10,Vec4Four());
													const vec4	Y204	=	Mul(Y20,Vec4Four());
													const vec4	Y134	=	Mul(Y13,Vec4Four());
													const vec4	Y234	=	Mul(Y23,Vec4Four());
													const vec4	Y304	=	Sub(Y104,Y204);
													const vec4	Y334	=	Sub(Y134,Y234);
													vec4 Visible	=	Vec4FFFFFFFF();
													uint16 y=MinY;
													do
													{
														vec4	P0x	=	Sub(Y10x,Mul(X10,dy4));
														vec4	P0y	=	Sub(Mul(X20,dy4),Y20x);
														vec4	P3x	=	Sub(Y13x,Mul(X13,dy4));
														vec4	P3y	=	Sub(Mul(X23,dy4),Y23x);
														uint16 x		=	MinX;
														vec4* 	pDstZ	=	reinterpret_cast<vec4*>(&m_ZBuffer[MinX+y*(uint16)SIZEX]);
														do
														{
															Prefetch<ECL_LVL1>(pDstZ);
															vec4 Mask	=	Or(Or(P0x,P0y),Or(P3x,P3y));
															vec4 rZ	=	*pDstZ++;
															Mask		=	Or(Mask,CmpLE(rZ,VMinZ));
															x				+=4;
															Visible	=	And(Visible,Mask);
															P0x			=	Add(P0x,Y104);
															P0y			=	Sub(P0y,Y204);
															P3x			=	Add(P3x,Y134);
															P3y			=	Sub(P3y,Y234);
														}while(x<MaxX);
														if(SignMask(Visible)!=(BitX|BitY|BitZ|BitW))
															return true;
														y++;
														dy4=Add(dy4,Vec4One());
													}while(y<MaxY);
													return false;
												}


	void									Show();
public:
#ifndef __SPU__
	ILINE									CCullRenderer()
												{
													m_DebugRender	=	0;
												}
#endif

	ILINE	void						Clear()
												{
													m_VMaxXY	=	NVMath::int32Tofloat(NVMath::Vec4(SIZEX,SIZEY,0,0));
													for(uint32 a=0,S=SIZEX*SIZEY;a<S;a++)
														m_ZBuffer[a]=9999999999.f;
													m_DrawCall=0;
													m_PolyCount=0;
												}

	bool DownLoadHWDepthBuffer(const Matrix44A& rCurrent, float nearPlane, float farPlane, float nearestMax, float Bias,float* pZBufferSwap=0)
	{
#if defined(XENON) || defined(WIN32) || defined(gringo)
		pZBufferSwap	=	m_ZBufferSwap;

		uint8 AlignBuffer[80];
		size_t pBuffer	=	(reinterpret_cast<size_t>(AlignBuffer)+15)&~15;
		Matrix44A& Reproject	=	*reinterpret_cast<Matrix44A*>(pBuffer);
#elif defined(PS3) || defined(LINUX) || defined(CAFE)
		Matrix44A	Reproject _ALIGN(16);
#endif

		m_VMaxXY	=	NVMath::int32Tofloat(NVMath::Vec4(SIZEX,SIZEY,0,0));
		const float a = farPlane / (farPlane - nearPlane);
		const float b = farPlane * nearPlane / (nearPlane - farPlane);

#ifdef PS3

#ifdef __SPU__
	//transfer zbuffer here
	uint8* HWZLocalBuf = (uint8*)alloca(SIZEX*SIZEY*sizeof(float) + 128);
	SPU_DOMAIN_LOCAL uint32 *pHWZBufferLocal = SPU_LOCAL_PTR((uint32*)(((uint32)HWZLocalBuf+127)&~127));
#if defined(CULLING_ENABLE_DEBUG_OVERLAY)
	if(Cry3DEngineBase::GetCVars()->e_CoverageBufferDebugFreeze)
	{
		memtransfer_from_main(pHWZBufferLocal,m_FrozenDephtBuffer,SIZEX*SIZEY*sizeof(float),3);//*id);
		memtransfer_sync(3);		
	}
	else
	{
		memtransfer_from_main(pHWZBufferLocal,HWZBuffer.pHardwareZBuffer,SIZEX*SIZEY*sizeof(float),3);//*id);
		memtransfer_sync(3);

		memtransfer_to_main(m_FrozenDephtBuffer,pHWZBufferLocal, SIZEX*SIZEY*sizeof(float),3);//*id);
		memtransfer_sync(3);
	}	
#else
	memtransfer_from_main(pHWZBufferLocal,HWZBuffer.pHardwareZBuffer,SIZEX*SIZEY*sizeof(float),3);//*id);
	memtransfer_sync(3);
#endif

	#define HWZBuf pHWZBufferLocal
#else
	#define HWZBuf HWZBuffer.pHardwareZBuffer
#endif
	for(uint32 y=0;y<SIZEY;y++)
	{
		//const uint32* __restrict pVMemZ	=	reinterpret_cast<uint32*>(&HWZBuf[(SIZEY-1-y)*SIZEX]);
		const uint32* __restrict pVMemZ	=	reinterpret_cast<uint32*>(&HWZBuf[y*SIZEX]);
		//for(uint32 x=0;x<SIZEX;x++)
		//	reinterpret_cast<uint32*>(m_ZBuffer)[x+y*SIZEX]	=	pVMemZ[x];

		for(uint32 x=0;x<SIZEX;x+=4)
		{
			uint32 Z0	=	pVMemZ[x];
			uint32 Z1	=	pVMemZ[x+1];
			uint32 Z2	=	pVMemZ[x+2];
			uint32 Z3	=	pVMemZ[x+3];
			m_ZBuffer[x+y*SIZEX]		=	static_cast<float>(Z0>>8)*(1.f/16777216.f);
			m_ZBuffer[x+1+y*SIZEX]	=	static_cast<float>(Z1>>8)*(1.f/16777216.f);
			m_ZBuffer[x+2+y*SIZEX]	=	static_cast<float>(Z2>>8)*(1.f/16777216.f);
			m_ZBuffer[x+3+y*SIZEX]	=	static_cast<float>(Z3>>8)*(1.f/16777216.f);
		}
	}

	memcpy(&Reproject,HWZBuf,16*sizeof(float));
	//{
	//	const float* pF	=	(float*)&Reproject;
	//	printf("M0: %2.2f %2.2f %2.2f %2.2f\n",pF[0],pF[1],pF[2],pF[3]);
	//	printf("M1: %2.2f %2.2f %2.2f %2.2f\n",pF[4],pF[5],pF[6],pF[7]);
	//	printf("M2: %2.2f %2.2f %2.2f %2.2f\n",pF[8],pF[9],pF[10],pF[11]);
	//	printf("M3: %2.2f %2.2f %2.2f %2.2f\n",pF[12],pF[13],pF[14],pF[15]);
	//}
	//
	//{
	//	const float* pF	=	(float*)&rCurrent;
	//	printf("W0: %2.2f %2.2f %2.2f %2.2f\n",pF[0],pF[1],pF[2],pF[3]);
	//	printf("W1: %2.2f %2.2f %2.2f %2.2f\n",pF[4],pF[5],pF[6],pF[7]);
	//	printf("W2: %2.2f %2.2f %2.2f %2.2f\n",pF[8],pF[9],pF[10],pF[11]);
	//	printf("W3: %2.2f %2.2f %2.2f %2.2f\n",pF[12],pF[13],pF[14],pF[15]);
	//}
	//printf("\n\n");
	//Reproject	=	GetTransposed44(Reproject);

#elif defined(XENON)

		uint16 buffer[SIZEX*SIZEY];
		Matrix44	Dummy;
		int gpuFrame = gEnv->pRenderer->GetOcclusionBuffer(&buffer[0], SizeX(), SizeY(), &Dummy,reinterpret_cast<Matrix44*>(&Reproject));

		{
		int i = 0;
#define STEPSIZE 8
		for(int y =  (SIZEY - 1); y>=0; y--)
		{
			for (int x = 0; x<SIZEX; x+=STEPSIZE,i+=STEPSIZE)
			{
				float* pF32	=	&m_ZBuffer[i];
				uint16* pU16	=	&buffer[x+y*SIZEX];
				NVMath::Prefetch<NVMath::ECL_LVL1>(pF32);
				uint16 u0	=	pU16[0];
				uint16 u1	=	pU16[1];
				uint16 u2	=	pU16[2];
				uint16 u3	=	pU16[3];
				uint16 u4	=	pU16[4];
				uint16 u5	=	pU16[5];
				uint16 u6	=	pU16[6];
				uint16 u7	=	pU16[7];
				float f0 = ((float)u0) * (1.f/65535.0f);
				float f1 = ((float)u1) * (1.f/65535.0f);
				float f2 = ((float)u2) * (1.f/65535.0f);
				float f3 = ((float)u3) * (1.f/65535.0f);
				float f4 = ((float)u4) * (1.f/65535.0f);
				float f5 = ((float)u5) * (1.f/65535.0f);
				float f6 = ((float)u6) * (1.f/65535.0f);
				float f7 = ((float)u7) * (1.f/65535.0f);
				pF32[0] = f0;
				pF32[1] = f1;
				pF32[2] = f2;
				pF32[3] = f3;
				pF32[4] = f4;
				pF32[5] = f5;
				pF32[6] = f6;
				pF32[7] = f7;
			}
		}
		}
#elif defined(WIN32) || defined(gringo)
		Matrix44	Dummy;
		if(!gEnv->pRenderer->GetOcclusionBuffer((uint16*)&m_ZBuffer[0], SizeX(), SizeY(), &Dummy,reinterpret_cast<Matrix44*>(&Reproject)))
			return false;

#endif
//#define USE_W_DEPTH
//#define SCALE_DEPTH

		int sizeX = SIZEX;
		int sizeY = SIZEY;

		float fWidth	= (float) sizeX;
		float fHeight = (float) sizeY;

		Matrix44A fromScreen;
		fromScreen.SetIdentity();
		fromScreen.SetTranslation(Vec3(-1.0f+0.5f/fWidth,1.0f-0.5f/fHeight,0.0f));
		fromScreen.m00 = 2.0f / fWidth;
		fromScreen.m11 = -2.0f / fHeight;  // Y flipped
		fromScreen.Transpose(); 

		Reproject.Invert();
		DEFINE_ALIGNED_DATA(Matrix44A, mToWorld, 16);
		mToWorld = fromScreen  * Reproject;

#if defined(XENON)
		size_t alignedSize = ((sizeX*sizeY*sizeof(float))+127) & ~127; // 128 byte alignment
		XMemSet128( pZBufferSwap, 0, alignedSize );
#else		
		memset(pZBufferSwap, 0, sizeX*sizeY*sizeof(float));
#endif

		{
			int x, y;
			float fY;
			using namespace NVMath;

#ifdef USE_W_DEPTH
			Matrix44A  mReproject = mToWorld * rCurrent;
			const vec4 MR0	=	reinterpret_cast<vec4*>(&mReproject)[0];
			const vec4 MR1	=	reinterpret_cast<vec4*>(&mReproject)[1];
			const vec4 MR2	=	reinterpret_cast<vec4*>(&mReproject)[2];
			const vec4 MR3	=	reinterpret_cast<vec4*>(&mReproject)[3];
#else
			const vec4 MW0	=	reinterpret_cast<vec4*>(&mToWorld)[0];
			const vec4 MW1	=	reinterpret_cast<vec4*>(&mToWorld)[1];
			const vec4 MW2	=	reinterpret_cast<vec4*>(&mToWorld)[2];
			const vec4 MW3	=	reinterpret_cast<vec4*>(&mToWorld)[3];

			const vec4 MS0	=	reinterpret_cast<const vec4*>(&rCurrent)[0];
			const vec4 MS1	=	reinterpret_cast<const vec4*>(&rCurrent)[1];
			const vec4 MS2	=	reinterpret_cast<const vec4*>(&rCurrent)[2];
			const vec4 MS3	=	reinterpret_cast<const vec4*>(&rCurrent)[3];
#endif
			const vec4 vA = NVMath::Vec4(a);
			const vec4 vB = NVMath::Vec4(b);

			const vec4 vMax = NVMath::Vec4(sizeX-0.0001f, sizeY-0.0001f, 1.0f, 9999999.9f );
			const vec4 vMin = NVMath::Vec4(0.0f, 0.0f, 0.0f, 0.0f);  

			const vec4 vXOffsets	= NVMath::Vec4(0.0f,1.0f,2.0f,3.0f);
			const vec4 vXIncrement = NVMath:: Vec4(4.0f);

			const vec4 vFarPlane	= NVMath::Vec4(farPlane);
			const vec4 vNegNearplane = NVMath::Vec4(-nearPlane);
			const vec4 vA_NegNear = Mul(vA, vNegNearplane);

			const vec4 vXAlign		=  NVMath::Vec4(0x3u,0x0u,0x0u,0x0u);
#if defined(WIN32) || defined(WIN64)
			const vec4 vNearest		=  NVMath:: Vec4(0.0001f); // There's no discernable nearest range in W depth, this is an estimation
#else
			const vec4 vNearest		=  NVMath:: Vec4(nearestMax);
#endif
#if defined(XENON)
			static vec4 vNearestLinear;
			float c = b / (nearestMax - a); // nearest depth in linear
			vNearestLinear.x = c, vNearestLinear.y = c, vNearestLinear.z = c,	vNearestLinear.w = c;
#else
			const vec4 vNearestLinear = NVMath::Vec4(b / (nearestMax - a)); // nearest depth in linear
#endif
			const vec4 vfEpsilon = NVMath::Vec4Epsilon();
			const vec4 vfOne = NVMath::Vec4One();
			const vec4 vZero = NVMath::Vec4Zero();

			vec4 vSelect[4];

			vSelect[0] = NVMath::Vec4(~0x0u, 0x0u, 0x0u, 0x0u);
			vSelect[1] = NVMath::Vec4( 0x0u,~0x0u, 0x0u, 0x0u);
			vSelect[2] = NVMath::Vec4( 0x0u, 0x0u,~0x0u, 0x0u);
			vSelect[3] = NVMath::Vec4( 0x0u, 0x0u, 0x0u,~0x0u);

			vec4* 	pSrcZ	=	reinterpret_cast<vec4*>(&m_ZBuffer[0]); 
			struct UVec4
			{
#if defined(XENON)				
				volatile 
#endif
				union
				{
					vec4  v;
					int32 i[4];
				};
			};
		 	struct 
			{
				UVec4 vAlign;
				UVec4 vFinalScreenPosU;
			} stored;
			for(y = 0, fY = 0.0f; y < sizeY; y++, fY+=1.0f)
			{	
				vec4 vYCoords = NVMath::Vec4(fY);
				vec4 vXCoords = vXOffsets;

				for(x=0; x < sizeX; x+=4)
				{

					vec4 vCurrentDepth = *pSrcZ;

#if defined(WIN32) || defined(WIN64)
					vec4 vWDepth = Mul(vCurrentDepth, vFarPlane);
					vec4 vZDepth = Madd(vWDepth, vA, vA_NegNear);
					vec4 vNonLinearDepth = Div(vZDepth, vWDepth);

					vec4 vIsNear			= NVMath::CmpLE(vCurrentDepth, vNearest);  
					vNonLinearDepth		= Select(vNonLinearDepth, vNearest, vIsNear);
#else
					vec4 vNonLinearDepth = vCurrentDepth; 
#endif

					vec4 vXXXX[4];
					vXXXX[0] = Swizzle<xxxx>(vXCoords);  
					vXXXX[1] = Swizzle<yyyy>(vXCoords);
					vXXXX[2] = Swizzle<zzzz>(vXCoords);
					vXXXX[3] = Swizzle<wwww>(vXCoords);

					vec4 vYYYY = vYCoords;

					vec4 vZZZZ[4];
					vZZZZ[0] = Swizzle<xxxx>(vNonLinearDepth);
					vZZZZ[1] = Swizzle<yyyy>(vNonLinearDepth);
					vZZZZ[2] = Swizzle<zzzz>(vNonLinearDepth);
					vZZZZ[3] = Swizzle<wwww>(vNonLinearDepth);

					for(int i = 0; i < 4; i++)
					{

#ifdef USE_W_DEPTH
							vec4 vScreenPos		= Madd(MR0, vXXXX[i], Madd(MR1,vYYYY, Madd(MR2, vZZZZ[i], MR3)));

							vec4 vScreenPosH	= Div(vScreenPos, Swizzle<wwww>(vScreenPos) );

							vec4 vNewDepth		= Div(vB,Sub(Swizzle<zzzz>(vScreenPosH), vA));
#else
							vec4 vWorldPos		= Madd(MW0, vXXXX[i], Madd(MW1,vYYYY, Madd(MW2, vZZZZ[i], MW3)));

							vec4 vWorldPosH		= Div(vWorldPos, Max(Swizzle<wwww>(vWorldPos), vfEpsilon));

							vec4 vScreenPos		= Madd(MS0, Swizzle<xxxx>(vWorldPosH), Madd(MS1,Swizzle<yyyy>(vWorldPosH), Madd(MS2, Swizzle<zzzz>(vWorldPosH), MS3)));

							vec4 vNewDepth		= Swizzle<zzzz>(vScreenPos);

							vec4 vScreenPosH	= Div(vScreenPos, Max(Swizzle<wwww>(vScreenPos),vfEpsilon));
#endif

							vec4 vOrigScreenPos = Shuffle<xzxz>( Shuffle<xxyy>(vXXXX[i], vYYYY), Shuffle<xxyy>(vZZZZ[i], vfOne ) );	
							vec4 vIsNearest			= NVMath::CmpLE(vZZZZ[i], vNearest);  // if less than  'NEAREST' range (gun) use current screen coords.

							vScreenPosH = Select(vScreenPosH, vOrigScreenPos, vIsNearest);
							vNewDepth		= Select(vNewDepth, vNearestLinear, vIsNearest);
 
							vec4 vOutBoundsMax = NVMath::CmpLE(vMax, vScreenPosH);
							vec4 vOutBoundsMin = NVMath::CmpLE(vScreenPosH, vMin);

							vec4 vOutBounds = Or(Swizzle<xyxy>(vOutBoundsMax), vOutBoundsMin);
							vOutBounds			= Or(Swizzle<xxxx>(vOutBounds), Or(Swizzle<yyyy>(vOutBounds),Or(Swizzle<zzzz>(vOutBounds), Swizzle<wwww>(vOutBounds))));

							vec4 vFinalScreenPos = Select(vScreenPosH, vZero, vOutBounds); // default to 0,0 if out of bounds

							vec4 vFinalScreenPosU = floatToint32(vFinalScreenPos);

							vec4 vAlign = And(vFinalScreenPosU, vXAlign);
				
							// LHS: Combine stores together for only one LHS (combines 4 LHS into 1)
							stored.vAlign.v 					= vAlign;
							stored.vFinalScreenPosU.v = vFinalScreenPosU;

							int element = stored.vAlign.i[0]; //Vec4int32(vAlign,0);
							int X = stored.vFinalScreenPosU.i[0] - element; //Vec4int32(vFinalScreenPosU,0) - element;  // todo, unsigned int vector sub
							int Y = stored.vFinalScreenPosU.i[1]; //Vec4int32(vFinalScreenPosU,1);

							vec4* 	pDstZ	=	reinterpret_cast<vec4*>(&pZBufferSwap[X+(Y*sizeX)]);
							NVMath::Prefetch<NVMath::ECL_LVL1>(pDstZ);

							vec4 vWriteMask = Select(vSelect[element], vZero, vOutBounds);

							vec4 vDstZ = *pDstZ;
							vec4 vDstZero = Select(vZero, vDstZ, vWriteMask);
							vec4 vNewDst = Select(vDstZ, vFarPlane, CmpLE(vDstZero, vZero));

							vNewDepth = Select(vNewDst, vNewDepth, CmpLE(vNewDepth,vNewDst));

							*pDstZ = Select(vDstZ, vNewDepth, vWriteMask);

					}
					vXCoords = Add(vXIncrement, vXCoords);

					pSrcZ++;
				}
			}


			vec4* 	pSwap	=	reinterpret_cast<vec4*>(&pZBufferSwap[0]);
			vec4* 	pDst	=	reinterpret_cast<vec4*>(&m_ZBuffer[0]);
	//	memcpy(pDst, pSwap	, sizeX*sizeY*sizeof(float));

			const vec4	vBiasAdd	=	NVMath::Vec4(Bias<0.f?-Bias:0.f);
			const vec4	vBiasMul	=	NVMath::Vec4(Bias>0.f?Bias:0.f);
			const int		pitchX		= SIZEX/4;

			vec4 vSideMask[4];

			vSideMask[0] = NVMath::Vec4( 0x0u,~0x0u, 0x0u, 0x0u);
			vSideMask[1] = NVMath::Vec4( 0x0u, 0x0u, 0x0u, 0x0u);
			vSideMask[2] = NVMath::Vec4( 0x0u, 0x0u, 0x0u, 0x0u);
			vSideMask[3] = NVMath::Vec4( 0x0u, 0x0u,~0x0u, 0x0u);

			vec4 vMiddleMask[4];

			vMiddleMask[0] = NVMath::Vec4(~0x0u,~0x0u, 0x0u, 0x0u);
			vMiddleMask[1] = NVMath::Vec4(~0x0u,~0x0u,~0x0u, 0x0u);
			vMiddleMask[2] = NVMath::Vec4( 0x0u,~0x0u,~0x0u,~0x0u);
			vMiddleMask[3] = NVMath::Vec4( 0x0u, 0x0u,~0x0u,~0x0u);

			for(y=0; y<sizeY; y++)
			{
				int minY = max((int)0,(int)y - 1);
				int maxY = min((int)sizeY-1,(int)y + 1);
				int vecX;
				for(x=0, vecX=0; x<sizeX; x+=4, vecX++)  //todo, fix edge cases
				{

					int minX = max(0,vecX - 1);
					int maxX = min(pitchX-1,vecX + 1);

					vec4 src[3][3];

					src[0][0]	= pSwap[minX	+ minY	* pitchX];
					src[0][1]	= pSwap[minX	+ y			* pitchX];
					src[0][2]	= pSwap[minX	+ maxY	* pitchX];

					src[1][0]	= pSwap[vecX	+ minY	* pitchX];
					src[1][1]	= pSwap[vecX  + y			* pitchX];
					src[1][2]	= pSwap[vecX	+ maxY	* pitchX];

					src[2][0]	= pSwap[maxX	+ minY	* pitchX];
					src[2][1]	= pSwap[maxX	+ y			* pitchX];
					src[2][2]	= pSwap[maxX	+ maxY	* pitchX];

					vec4 vDst = Vec4Zero();

					for(int i = 0; i < 4; i++)
					{
						vec4 vSides[3], vMiddle[3];

						vSides[0] = Select(Vec4Zero(), Shuffle<zwxy>(src[0][0], src[2][0]), vSideMask[i]);
						vSides[1] = Select(Vec4Zero(), Shuffle<zwxy>(src[0][1], src[2][1]), vSideMask[i]);
						vSides[2] = Select(Vec4Zero(), Shuffle<zwxy>(src[0][2], src[2][2]), vSideMask[i]);

						vMiddle[0] = Select(Vec4Zero(), src[1][0] , vMiddleMask[i]);
						vMiddle[1] = Select(Vec4Zero(), src[1][1] , vMiddleMask[i]);
						vMiddle[2] = Select(Vec4Zero(), src[1][2] , vMiddleMask[i]);

						vec4 _vMax[3];
						_vMax[0] = Max(vSides[0], vMiddle[0]);
						_vMax[1] = Max(vSides[1], vMiddle[1]);
						_vMax[2] = Max(vSides[2], vMiddle[2]);

						_vMax[0] = Max(Max(_vMax[0], _vMax[1]), _vMax[2]);

						_vMax[1] = Max(_vMax[0], Swizzle<zwxy>(_vMax[0]));
						_vMax[0] = Max(_vMax[1], Swizzle<wzyx>(_vMax[1]));


						vec4 vWriteMask = vSelect[i];
						vec4 vPixelSrc = src[1][1];

						vec4 vSrcIsZero = Select(Vec4Zero(), Vec4FFFFFFFF(), CmpLE(vPixelSrc, Vec4Zero()) );
						vec4 vMaxIsZero = Select(Vec4Zero(), Vec4FFFFFFFF(), CmpLE(_vMax[0],		Vec4Zero()) );

						vec4 vMaxFinal = Select(_vMax[0], vFarPlane, vMaxIsZero);
						vec4 vOut = Select(vPixelSrc, vMaxFinal, vSrcIsZero);

						vDst = Select(vDst, vOut, vWriteMask);

					}

					vDst	=	Add(vDst,vBiasAdd);//linear bias
					vDst	=	Add(vDst,Madd(vBiasMul,vDst,vBiasMul));// none-linear bias
#ifdef SCALE_DEPTH
					//*pDst = Mul(vDst, NVMath::Vec4(1.2f));
					*pDst = Add(vDst, NVMath::Vec4(0.5f));
#else
					*pDst = vDst;
#endif
					pDst++;

				}
			}

		}
	//for(int a=0;a<128;a+=16)
	//	printf("%2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f %2.2f\n",
	//	m_ZBuffer[a+0],m_ZBuffer[a+1],m_ZBuffer[a+2],m_ZBuffer[a+3],
	//	m_ZBuffer[a+4],m_ZBuffer[a+5],m_ZBuffer[a+6],m_ZBuffer[a+7],
	//	m_ZBuffer[a+8],m_ZBuffer[a+9],m_ZBuffer[a+10],m_ZBuffer[a+11],
	//	m_ZBuffer[a+12],m_ZBuffer[a+13],m_ZBuffer[a+14],m_ZBuffer[a+15]);
#if defined(__SPU__) && defined(CULLING_ENABLE_DEBUG_OVERLAY)
	memtransfer_to_main(m_FrozenDephtBufferReprojected,m_ZBuffer,SIZEX*SIZEY*sizeof(float),3);
	memtransfer_sync(3);
#endif
#ifdef CULL_RENDERER_REPROJ_DEBUG
		memcpy(pZBufferSwap, m_ZBuffer, sizeX*sizeY*sizeof(float));
#endif


#ifdef SCALE_DEPTH
#undef SCALE_DEPTH
#endif

#ifdef USE_W_DEPTH
#undef USE_W_DEPTH
#endif
		return true;
	}

	ILINE	bool						TestAABB(const NVMath::vec4* pViewProj,Vec3 Min,Vec3 Max,Vec3 ViewPos)
												{
													using namespace NVMath;
													const NVMath::vec4 M0	=	pViewProj[0];
													const NVMath::vec4 M1	=	pViewProj[1];
													const NVMath::vec4 M2	=	pViewProj[2];
													const NVMath::vec4 M3	=	pViewProj[3];
													const NVMath::vec4 MinX	=	NVMath::Vec4(Min.x);
													const NVMath::vec4 MinY	=	NVMath::Vec4(Min.y);
													const NVMath::vec4 MinZ	=	NVMath::Vec4(Min.z);
													const NVMath::vec4 MaxX	=	NVMath::Vec4(Max.x);
													const NVMath::vec4 MaxY	=	NVMath::Vec4(Max.y);
													const NVMath::vec4 MaxZ	=	NVMath::Vec4(Max.z);

													const vec4	VB0		=	Madd(MinX,M0,Madd(MinY,M1,Madd(MinZ,M2,M3)));//NVMath::Vec4(rAABB.min.x,rAABB.min.y,rAABB.min.z,1.f),
													const vec4	VB1		=	Madd(MinX,M0,Madd(MaxY,M1,Madd(MinZ,M2,M3)));//NVMath::Vec4(rAABB.min.x,rAABB.max.y,rAABB.min.z,1.f),
													const vec4	VB2		=	Madd(MaxX,M0,Madd(MinY,M1,Madd(MinZ,M2,M3)));//NVMath::Vec4(rAABB.max.x,rAABB.min.y,rAABB.min.z,1.f),
													const vec4	VB3		=	Madd(MaxX,M0,Madd(MaxY,M1,Madd(MinZ,M2,M3)));//NVMath::Vec4(rAABB.max.x,rAABB.max.y,rAABB.min.z,1.f),
													const vec4	VB4		=	Madd(MinX,M0,Madd(MinY,M1,Madd(MaxZ,M2,M3)));//NVMath::Vec4(rAABB.min.x,rAABB.min.y,rAABB.max.z,1.f),
													const vec4	VB5		=	Madd(MinX,M0,Madd(MaxY,M1,Madd(MaxZ,M2,M3)));//NVMath::Vec4(rAABB.min.x,rAABB.max.y,rAABB.max.z,1.f),
													const vec4	VB6		=	Madd(MaxX,M0,Madd(MinY,M1,Madd(MaxZ,M2,M3)));//NVMath::Vec4(rAABB.max.x,rAABB.min.y,rAABB.max.z,1.f),
													const vec4	VB7		=	Madd(MaxX,M0,Madd(MaxY,M1,Madd(MaxZ,M2,M3)));//NVMath::Vec4(rAABB.max.x,rAABB.max.y,rAABB.max.z,1.f)};
													vec4 SMask	=	Or(Or(Or(VB0,VB1),Or(VB2,VB3)),Or(Or(VB4,VB5),Or(VB6,VB7)));

													if(SignMask(SMask)&BitZ)
													{
														if(Max.x<ViewPos.x)
														{
															if(Triangle<false,false>(VB3,VB2,VB7))return true;	//MaxX		
															if(Triangle<false,false>(VB7,VB2,VB6))return true;			
														}																 
														if(Min.x>ViewPos.x)						 
														{																 
															if(Triangle<false,false>(VB0,VB1,VB4))return true;	//MinX		
															if(Triangle<false,false>(VB4,VB1,VB5))return true;			
														}																 
														if(Max.y<ViewPos.y)					 
														{																 
															if(Triangle<false,false>(VB1,VB3,VB5))return true;	//MaxY		
															if(Triangle<false,false>(VB5,VB3,VB7))return true;			
														}																 
														if(Min.y>ViewPos.y)						 
														{																 
															if(Triangle<false,false>(VB2,VB0,VB6))return true;	//MinY		
															if(Triangle<false,false>(VB6,VB0,VB4))return true;			
														}																 
														if(Max.z<ViewPos.z)						 
														{																 
															if(Triangle<false,false>(VB4,VB5,VB6))return true;	//MaxZ		
															if(Triangle<false,false>(VB6,VB5,VB7))return true;			
														}																 
														if(Min.z>ViewPos.z)					 
														{																 
															if(Triangle<false,false>(VB1,VB0,VB3))return true;	//MinZ		
															if(Triangle<false,false>(VB3,VB0,VB2))return true;			
														}
													}											
													else
													{
														if(Max.x<ViewPos.x)
														{
															//if(Quad2D(VB3,VB2,VB6,VB7))return true;			
															if(Triangle2D<false,false>(VB3,VB2,VB7))return true;			
															if(Triangle2D<false,false>(VB7,VB2,VB6))return true;			
														}													
														if(Min.x>ViewPos.x)				
														{													
															//if(Quad2D(VB0,VB1,VB5,VB4))return true;			
															if(Triangle2D<false,false>(VB0,VB1,VB4))return true;			
															if(Triangle2D<false,false>(VB4,VB1,VB5))return true;			
														}													
														if(Max.y<ViewPos.y)				
														{													
															//if(Quad2D(VB1,VB3,VB7,VB5))return true;			
															if(Triangle2D<false,false>(VB1,VB3,VB5))return true;			
															if(Triangle2D<false,false>(VB5,VB3,VB7))return true;			
														}													
														if(Min.y>ViewPos.y)				
														{													
															//if(Quad2D(VB2,VB0,VB4,VB6))return true;			
															if(Triangle2D<false,false>(VB2,VB0,VB6))return true;			
															if(Triangle2D<false,false>(VB6,VB0,VB4))return true;			
														}													
														if(Max.z<ViewPos.z)				
														{													
															//if(Quad2D(VB4,VB5,VB7,VB6))return true;			
															if(Triangle2D<false,false>(VB4,VB5,VB6))return true;			
															if(Triangle2D<false,false>(VB6,VB5,VB7))return true;			
														}													
														if(Min.z>ViewPos.z)				
														{													
															//if(Quad2D(VB1,VB0,VB2,VB3))return true;			
															if(Triangle2D<false,false>(VB1,VB0,VB3))return true;			
															if(Triangle2D<false,false>(VB3,VB0,VB2))return true;			
														}
													}
													return false;

												}
	template<bool WRITE>
	ILINE	bool						Rasterize(	const NVMath::vec4* pViewProj, tdVertexCacheArg vertexCache,
																		const tdIndex*	__restrict pIndices,const uint32 ICount,
																		const uint8*		__restrict pVertices,const uint32 VertexSize,const uint32 VCount)
												{
													using namespace NVMath;
													if(!VCount || !ICount)
														return false;

													m_DrawCall++;
													m_PolyCount+=VCount/3;

													const vec4 M0	=	pViewProj[0];
													const vec4 M1	=	pViewProj[1];
													const vec4 M2	=	pViewProj[2];
													const vec4 M3	=	pViewProj[3];
#if defined(PS3)
													if(VCount+1>VERTEX_CACHE_COUNT)
														return true;
													SPU_DOMAIN_LOCAL vec4* const pVCache	=	SPU_LOCAL_PTR(vertexCache);
#else
													if(VCount+1>vertexCache.size())
														vertexCache.resize(VCount+1);
													vec4* pVCache	=	&vertexCache[0];
													pVCache	=	reinterpret_cast<vec4*>(((reinterpret_cast<size_t>(pVCache)+15)&~15));
#endif
													vec4 SMask	=	Vec4Zero();
													for(uint32 a=0,S=VCount&~3;a<S;a+=4)
													{
														const float* pV0	=	reinterpret_cast<const float*>(pVertices+a*VertexSize);
														const float* pV1	=	reinterpret_cast<const float*>(pVertices+(a+1)*VertexSize);
														const float* pV2	=	reinterpret_cast<const float*>(pVertices+(a+2)*VertexSize);
														const float* pV3	=	reinterpret_cast<const float*>(pVertices+(a+3)*VertexSize);
														const vec4 V0	=	Madd(NVMath::Vec4(pV0[0]),M0,Madd(NVMath::Vec4(pV0[1]),M1,Madd(NVMath::Vec4(pV0[2]),M2,M3)));
														const vec4 V1	=	Madd(NVMath::Vec4(pV1[0]),M0,Madd(NVMath::Vec4(pV1[1]),M1,Madd(NVMath::Vec4(pV1[2]),M2,M3)));
														const vec4 V2	=	Madd(NVMath::Vec4(pV2[0]),M0,Madd(NVMath::Vec4(pV2[1]),M1,Madd(NVMath::Vec4(pV2[2]),M2,M3)));
														const vec4 V3	=	Madd(NVMath::Vec4(pV3[0]),M0,Madd(NVMath::Vec4(pV3[1]),M1,Madd(NVMath::Vec4(pV3[2]),M2,M3)));
														SMask	=	Or(SMask,V0);
														SMask	=	Or(SMask,V1);
														SMask	=	Or(SMask,V2);
														SMask	=	Or(SMask,V3);
														pVCache[a  ]	=	V0;
														pVCache[a+1]	=	V1;
														pVCache[a+2]	=	V2;
														pVCache[a+3]	=	V3;
													}
													for(uint32 a=VCount&~3,S=VCount;a<S;a++)
													{
														const float* pV	=	reinterpret_cast<const float*>(pVertices+a*VertexSize);
														const vec4 V	=	Madd(NVMath::Vec4(pV[0]),M0,Madd(NVMath::Vec4(pV[1]),M1,Madd(NVMath::Vec4(pV[2]),M2,M3)));
														SMask	=	Or(SMask,V);
														pVCache[a]		=	V;
													}

													bool Visible=false;
													if(SignMask(SMask)&BitZ)
														for(uint32 a=0;a<ICount;a+=3)
														{
															vec4 Pos0	=	pVCache[pIndices[a]];
															vec4 Pos2	=	pVCache[pIndices[a+1]];
															vec4 Pos1	=	pVCache[pIndices[a+2]];
															Visible|=Triangle<WRITE,true>(Pos0,Pos1,Pos2);
															if(!WRITE && Visible)
																return true;			
														}											
													else										
														for(uint32 a=0;a<ICount;a+=3)
														{											
															vec4 Pos0	=	pVCache[pIndices[a]];
															vec4 Pos2	=	pVCache[pIndices[a+1]];
															vec4 Pos1	=	pVCache[pIndices[a+2]];
															Visible|=Triangle2D<WRITE,true>(Pos0,Pos1,Pos2);
															if(!WRITE && Visible)
																return true;
														}
														return Visible;
												}

#ifndef __SPU__
	int m_DebugRender;

	void									DrawDebug(IRenderer* pRenderer,int32 nStep)
												{ // project buffer to the screen
#if defined(CULLING_ENABLE_DEBUG_OVERLAY)
													nStep	%=	32;
													if(!nStep)
														return;

													if(!m_DebugRender)
														return;

													const float FarPlaneInv	=	255.f/pRenderer->GetCamera().GetFarPlane();
													
													SAuxGeomRenderFlags oFlags( e_Def2DPublicRenderflags );
													oFlags.SetDepthTestFlag( e_DepthTestOff );
													oFlags.SetDepthWriteFlag( e_DepthWriteOff );
													oFlags.SetCullMode( e_CullModeNone );
													oFlags.SetAlphaBlendMode( e_AlphaNone );													
													pRenderer->GetIRenderAuxGeom()->SetRenderFlags(oFlags);

													int nScreenHeight = gEnv->pRenderer->GetHeight();
													int nScreenWidth = gEnv->pRenderer->GetWidth();

													float fScreenHeight = (float)nScreenHeight;
													float fScreenWidth = (float)nScreenWidth;
												
													float fTopOffSet = 35.0f;
													float fSideOffSet = 35.0f;

													// draw z-buffer as we get it from the GPU
													for(uint32 y=0; y<SIZEY; y+=1)
													{
														const uint32* __restrict pVMemZ	=	alias_cast<uint32*>(&m_FrozenDephtBuffer[y*SIZEX]);
														float fY= fTopOffSet + (y*3);
														for(uint32 x=0; x<SIZEX; x+=4)
														{
															float fX0= fSideOffSet + ((x+0)*3);
															float fX1= fSideOffSet + ((x+1)*3);
															float fX2= fSideOffSet + ((x+2)*3);
															float fX3= fSideOffSet + ((x+3)*3);
																
															uint32 Z0	=	pVMemZ[x];
															uint32 Z1	=	pVMemZ[x+1];
															uint32 Z2	=	pVMemZ[x+2];
															uint32 Z3	=	pVMemZ[x+3];
																
															uint32 ValueColor0  = (uint32)(static_cast<float>(Z0>>8)*(1.f/16777216.f)*340.0f);
															uint32 ValueColor1  = (uint32)(static_cast<float>(Z1>>8)*(1.f/16777216.f)*340.0f);
															uint32 ValueColor2  = (uint32)(static_cast<float>(Z2>>8)*(1.f/16777216.f)*340.0f);
															uint32 ValueColor3  = (uint32)(static_cast<float>(Z3>>8)*(1.f/16777216.f)*340.0f);
															ColorB color0=ColorB(ValueColor0,ValueColor0,ValueColor0,222);
															ColorB color1=ColorB(ValueColor1,ValueColor1,ValueColor1,222);
															ColorB color2=ColorB(ValueColor2,ValueColor2,ValueColor2,222);
															ColorB color3=ColorB(ValueColor3,ValueColor3,ValueColor3,222);
																
															NAsyncCull::Debug::Draw2DBox(fX0,fY,3.0f,3.0f,color0, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX1,fY,3.0f,3.0f,color1, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX2,fY,3.0f,3.0f,color2, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX3,fY,3.0f,3.0f,color3, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
														}
													}

													// draw z-buffer after reprojection (unknown parts are red)
													fTopOffSet += 200.0f;
													for(uint32 y=0; y<SIZEY; y+=1)
													{
														const float* __restrict pVMemZ	=	alias_cast<float*>(&m_FrozenDephtBufferReprojected[y*SIZEX]);
														float fY= fTopOffSet + (y*3);
														for(uint32 x=0; x<SIZEX; x+=4)
														{
															float fX0= fSideOffSet + ((x+0)*3);
															float fX1= fSideOffSet + ((x+1)*3);
															float fX2= fSideOffSet + ((x+2)*3);
															float fX3= fSideOffSet + ((x+3)*3);
																
															uint32 ValueColor0  = (uint32)(pVMemZ[x+0]);
															uint32 ValueColor1  = (uint32)(pVMemZ[x+1]);
															uint32 ValueColor2  = (uint32)(pVMemZ[x+2]);
															uint32 ValueColor3  = (uint32)(pVMemZ[x+3]);
															ColorB color0=ColorB(ValueColor0,ValueColor0,ValueColor0,222);
															ColorB color1=ColorB(ValueColor1,ValueColor1,ValueColor1,222);
															ColorB color2=ColorB(ValueColor2,ValueColor2,ValueColor2,222);
															ColorB color3=ColorB(ValueColor3,ValueColor3,ValueColor3,222);
																
															NAsyncCull::Debug::Draw2DBox(fX0,fY,3.0f,3.0f,color0, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX1,fY,3.0f,3.0f,color1, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX2,fY,3.0f,3.0f,color2, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
															NAsyncCull::Debug::Draw2DBox(fX3,fY,3.0f,3.0f,color3, fScreenHeight,fScreenWidth,pRenderer->GetIRenderAuxGeom());
														}
													}
#endif
												}
#endif
	ILINE	uint32					SizeX()const{return SIZEX;}
	ILINE	uint32					SizeY()const{return SIZEY;}

};
}

#pragma warning(pop)

#if defined(CULLING_ENABLE_DEBUG_OVERLAY)
template<uint32 SIZEX,uint32 SIZEY>
float NAsyncCull::CCullRenderer<SIZEX,SIZEY>::m_FrozenDephtBuffer[SIZEX*SIZEY] _ALIGN(128);

template<uint32 SIZEX,uint32 SIZEY>
float NAsyncCull::CCullRenderer<SIZEX,SIZEY>::m_FrozenDephtBufferReprojected[SIZEX*SIZEY] _ALIGN(128);
#endif

#endif

